﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using CoreGraphics;
using mb;
using UIKit;

namespace mb
{
    public class PagerViewManager : IDisposable
    {
        static ILog logger = mb.ServiceLocator.Instance.Get<ILog>();
        #region Sub classes

        public class PageChangedEventArgs : EventArgs
        {
            public int PageIndex { get; }

            public PageChangedEventArgs(int pageIndex)
            {
                PageIndex = pageIndex;
            }
        }

        public class PageChangingEventArgs : EventArgs
        {
            public int PageIndex { get; }

            public PageChangingEventArgs(int pageIndex)
            {
                PageIndex = pageIndex;
            }
        }

        #endregion

        #region Fields

        private UIViewController _parentViewController;
        private UIView _pagesContainer;
        private UIView _buttonsContainer;
        private UINavigationBar _navBar;
        private List<UIViewController> _pagerViewControllers = new List<UIViewController>();
        private List<UIButton> _pagerButtons = new List<UIButton>();
        public UIPanGestureRecognizer _panGestureRecognizer;
        private List<nfloat> _dx = new List<nfloat>();
        private bool _swipeEnabled;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the page slide speed.
        /// </summary>
        /// <value>The page slide speed.</value>
        public nfloat SlideSpeed { get; set; } = 0.2f;

        /// <summary>
        /// Gets or sets the pager buttons margin.
        /// </summary>
        /// <value>The pager buttons margin.</value>
        public nfloat PagerButtonsMargin { get; set; } = 0f;

        /// <summary>
        /// Gets the index of the current view controller.
        /// </summary>
        /// <value>The index of the current view controller.</value>
        public int CurrentViewControllerIndex { get; private set; }

        /// <summary>
        /// Gets the view controllers count.
        /// </summary>
        /// <value>The view controllers count.</value>
        public int ViewControllersCount => _pagerViewControllers.Count;

        /// <summary>
        /// Gets the list of view controllers.
        /// </summary>
        /// <value>The list of view controllers.</value>
        public IReadOnlyList<UIViewController> ViewControllers => _pagerViewControllers;

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="T:PagerManager.PagerViewManager"/> enable swipe.
        /// </summary>
        /// <value><c>true</c> if enable swipe; otherwise, <c>false</c>.</value>
        public bool EnableSwipe
        {
            get => _swipeEnabled;
            set
            {
                if (_swipeEnabled == value) return;
                _swipeEnabled = value;
                var GRView = _parentViewController.View;
                GRView = _pagesContainer;
                if (_swipeEnabled)
                {
                    GRView.AddGestureRecognizer(_panGestureRecognizer);
                }
                else
                {
                    GRView.RemoveGestureRecognizer(_panGestureRecognizer);
                }
            }
        }

        #endregion

        #region Events

        /// <summary>
        /// Occurs when page changed.
        /// </summary>
        public event EventHandler<PageChangedEventArgs> PageChanged;

        /// <summary>
        /// Occurs when page is changing.
        /// </summary>
        public event EventHandler<PageChangingEventArgs> PageChanging;

        #endregion

        #region Constructor

        public PagerViewManager(UIViewController parentViewController, UIView pagesContainer, bool swipeGesture = true)
        {
            Contract.Requires(pagesContainer != null);
            Contract.Requires(parentViewController != null);

            _panGestureRecognizer = new UIPanGestureRecognizer(PanHandler);
            _parentViewController = parentViewController;
            _pagesContainer = pagesContainer;
            EnableSwipe = swipeGesture;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Sets the buttons container.
        /// </summary>
        /// <param name="buttonsContainer">Buttons container.</param>
        public void SetButtonsContainer(UIView buttonsContainer)
        {
            _buttonsContainer = buttonsContainer;
        }

        /// <summary>
        /// Sets the shared navigation bar.
        /// </summary>
        /// <param name="navBar">Nav bar.</param>
        public void SetSharedNavigationBar(UINavigationBar navBar)
        {
            _navBar = navBar;
        }

        /// <summary>
        /// Adds the page view controller.
        /// </summary>
        /// <param name="viewController">View controller.</param>
        /// <param name="button">Button.</param>
        public void AddPageViewController(UIViewController viewController, UIButton button)
        {
            _pagerViewControllers.Add(viewController);
            _parentViewController.AddChildViewController(viewController);
            _dx.Add(0);
            viewController.DidMoveToParentViewController(_parentViewController);
            SetFrameForChildViewController(viewController);
            _pagesContainer.AddSubview(viewController.View);

            if (_buttonsContainer != null)
            {
                _buttonsContainer.AddSubview(button);
                _pagerButtons.Add(button);
            }
        }

        /// <summary>
        /// Inserts the page view controller.
        /// </summary>
        /// <param name="viewController">View controller.</param>
        /// <param name="button">Button.</param>
        /// <param name="index">Index.</param>
        public void InsertPageViewController<T>(T viewController, UIButton button, int index)
        where T : UIViewController //, IPageViewController
        {
            if (index <= CurrentViewControllerIndex)
                CurrentViewControllerIndex++;

            _pagerViewControllers.Insert(index, viewController);
            _parentViewController.AddChildViewController(viewController);
            viewController.DidMoveToParentViewController(_parentViewController);
            _dx.Insert(index, 0);
            _pagesContainer.AddSubview(viewController.View);

            if (_buttonsContainer != null)
            {
                _buttonsContainer.AddSubview(button);
                _pagerButtons.Insert(index, button);
            }

            UpdateFrames(null);
        }

        /// <summary>
        /// Removes the page view controller.
        /// </summary>
        /// <param name="index">Index.</param>
        public void RemovePageViewController(int index)
        {
            if (index < CurrentViewControllerIndex)
                CurrentViewControllerIndex--;

            var pageViewControllerToRemove = _pagerViewControllers[index];
            _pagerViewControllers.RemoveAt(index);
            pageViewControllerToRemove.RemoveFromParentViewController();
            _dx.RemoveAt(index);
            pageViewControllerToRemove.View.RemoveFromSuperview();

            if (_buttonsContainer != null)
            {
                _pagerButtons[index].RemoveFromSuperview();
                _pagerButtons.RemoveAt(index);
            }

            UpdateFrames(null);
        }

        /// <summary>
        /// Sets the current page.
        /// </summary>
        /// <param name="index">Index.</param>
        /// <param name="animate">If set to <c>true</c> animate.</param>
        public void SetCurrentPage(int index, bool animate = true)
        {
            CurrentViewControllerIndex = index;
            PageChanging?.Invoke(this, new PageChangingEventArgs(CurrentViewControllerIndex));
            UpdateFrames(() =>
                {
                    // if (_pagerViewControllers[CurrentViewControllerIndex] is IPageViewController pageViewController)
                    // {
                    //     if (_navBar != null)
                    //         _navBar.TopItem.RightBarButtonItem = pageViewController.RightBarButtonItem;
                    // }

                    PageChanged?.Invoke(this, new PageChangedEventArgs(CurrentViewControllerIndex));
                },
                animate);
            UpdateButtonsState();
        }

        /// <summary>
        /// Refreshs the current page.
        /// </summary>
        public void RefreshCurrentPage()
        {
            // if (_pagerViewControllers[CurrentViewControllerIndex] is IPageViewController pageViewController)
            // {
            //     pageViewController.Refresh();
            // }
        }

        /// <summary>
        /// Updates the frames.
        /// </summary>
        /// <param name="animate">If set to <c>true</c> animate.</param>
        public void UpdateFrames(Action competion, bool animate = false)
        {
            UpdateFramesForPagerButtons(animate);
            UpdateFramesForChildViewControllers(competion, animate);
        }

        /// <summary>
        /// Gets index of the controller specified.
        /// </summary>
        /// <returns>View controller index.</returns>
        /// <param name="viewController">View controller.</param>
        public int IndexOf(UIViewController viewController)
        {
            return _pagerViewControllers.IndexOf(viewController);
        }

        public void Dispose()
        {
            if (_pagerViewControllers != null)
            {
                foreach (var viewController in _pagerViewControllers)
                {
                    viewController?.View?.RemoveFromSuperview();
                    viewController?.RemoveFromParentViewController();
                }
                _pagerViewControllers = null;
            }

            if (_pagerButtons != null)
            {
                foreach (var button in _pagerButtons)
                {
                    button?.RemoveFromSuperview();
                }
                _pagerButtons = null;
            }

            if (_parentViewController != null)
            {
                if (_parentViewController.View != null)
                {
                    if (_panGestureRecognizer != null)
                    {
                        _parentViewController.View.RemoveGestureRecognizer(_panGestureRecognizer);
                    }
                }
            }

            if (_panGestureRecognizer != null)
            {
                _parentViewController = null;
            }

            if (_pagesContainer != null)
            {
                _pagesContainer = null;
            }
        }

        private void SetFrameForChildViewController(UIViewController viewController)
        {
            GetFrameForChildViewController(viewController).Invoke();
        }

        private Action GetFrameForChildViewController(UIViewController viewController)
        {
            var index = _pagerViewControllers.IndexOf(viewController);
            if (index == CurrentViewControllerIndex)
            {
                return () => SetFrameForChildViewControllerToCenter(viewController);
            }
            else if (index < CurrentViewControllerIndex)
            {
                return () => SetFrameForChildViewControllerToLeft(viewController);
            }
            else if (index > CurrentViewControllerIndex)
            {
                return () => SetFrameForChildViewControllerToRight(viewController);
            }
            return null;
        }

        private void SetFrameForChildViewControllerToCenter(UIViewController viewController)
        {
            viewController.View.Frame = new CGRect(
                0,
                0,
                _pagesContainer.Frame.Width,
                _pagesContainer.Frame.Height);
        }

        private void SetFrameForChildViewControllerToLeft(UIViewController viewController)
        {
            viewController.View.Frame = new CGRect(-_pagesContainer.Frame.Width,
                0,
                _pagesContainer.Frame.Width,
                _pagesContainer.Frame.Height);
        }

        private void SetFrameForChildViewControllerToRight(UIViewController viewController)
        {
            viewController.View.Frame = new CGRect(
                _pagesContainer.Frame.Width,
                0,
                _pagesContainer.Frame.Width,
                _pagesContainer.Frame.Height);
        }

        private void UpdateButtonsState()
        {
            if (_buttonsContainer == null) return;

            foreach (var button in _pagerButtons)
            {
                var index = _pagerButtons.IndexOf(button);
                if (index == CurrentViewControllerIndex)
                    button.Selected = true;
                else
                    button.Selected = false;
            }
        }

        private void UpdateFramesForPagerButtonsFixed(bool animate = false)
        {
            if (_buttonsContainer == null) return;

            var containerWidth = (float) _buttonsContainer.Bounds.Width;
            var containerHeight = _buttonsContainer.Bounds.Height;
            // var totalWidth = _pagerButtons.Count * _pagerButtons[0].Bounds.Width;
            float itemWidth = 1.0f * containerWidth / _pagerButtons.Count;
            float nextStart = 0;
            foreach (var btn in _pagerButtons)
            {
                var frame = new CGRect(nextStart, 0, itemWidth, containerHeight);
                btn.Frame = frame;
                nextStart += itemWidth;
            }
        }

        private void UpdateFramesForPagerButtons(bool animate = false)
        {
            if (_buttonsContainer == null) return;
            if (true)
            {
                UpdateFramesForPagerButtonsFixed(animate);
                return;
            }

            List<Action> actions = new List<Action>();
            // set frame for central button
            var centralButton = _pagerButtons[CurrentViewControllerIndex];
            var centralFrame = new CGRect(
                _buttonsContainer.Center.X - centralButton.Frame.Width / 2,
                0,
                centralButton.Frame.Width,
                _buttonsContainer.Frame.Height);
            actions.Add(() =>
            {
                centralButton.Frame = centralFrame;
            });

            // set frames for right buttons       
            var nextButtonStart = centralFrame.Right + PagerButtonsMargin;
            for (var i = CurrentViewControllerIndex + 1; i < _pagerButtons.Count; i++)
            {
                var button = _pagerButtons[i];
                var frame = new CGRect(
                    nextButtonStart,
                    0,
                    button.Frame.Width,
                    _buttonsContainer.Frame.Height);
                actions.Add(() =>
                {
                    button.Frame = frame;
                    // logger.Log("btn frame", button, frame);
                });

                nextButtonStart += frame.Width + PagerButtonsMargin;
            }
            // set frames for left buttons       
            var prevButtonEnd = centralFrame.Left - PagerButtonsMargin;
            for (var i = CurrentViewControllerIndex - 1; i >= 0; i--)
            {
                var button = _pagerButtons[i];
                var frame = new CGRect(
                    prevButtonEnd - button.Frame.Width,
                    0,
                    button.Frame.Width,
                    _buttonsContainer.Frame.Height);
                actions.Add(() =>
                {
                    button.Frame = frame;
                    // logger.Log("btn frame", button, frame);
                });

                prevButtonEnd -= frame.Width + PagerButtonsMargin;
            }

            foreach (var action in actions)
            {
                if (animate)
                {
                    UIView.Animate(
                        SlideSpeed,
                        0,
                        UIViewAnimationOptions.CurveEaseInOut,
                        action,
                        () => { });
                }
                else
                {
                    action();
                }
            }
        }

        private void UpdateFramesForChildViewControllers(Action completion, bool animate = false)
        {
            var frameChanges = new List<Action>();
            foreach (var viewController in _pagerViewControllers)
            {
                frameChanges.Add(GetFrameForChildViewController(viewController));
            }

            if (animate)
            {
                UIView.Animate(
                    SlideSpeed,
                    0,
                    UIViewAnimationOptions.CurveEaseInOut,
                    () =>
                    {
                        foreach (var action in frameChanges)
                        {
                            action.Invoke();
                        }
                    },
                    () =>
                    {
                        completion?.Invoke();
                    });
            }
            else
            {
                foreach (var action in frameChanges)
                {
                    action.Invoke();
                }
                completion?.Invoke();
            }
        }

        private void MovePage(int pageIndex, CGPoint point)
        {
            // logger.Log("MovePag", pageIndex, point);
            if (_dx[pageIndex] == 0)
            {
                _dx[pageIndex] = point.X - _pagerViewControllers[pageIndex].View.Center.X;
            }

            if (pageIndex == 0 &&
                point.X - _dx[pageIndex] > _pagesContainer.Frame.Width / 2)
            {
                return;
            }

            if (pageIndex == _pagerViewControllers.Count - 1 &&
                point.X - _dx[pageIndex] < _pagesContainer.Frame.Width / 2)
            {
                return;
            }

            _pagerViewControllers[pageIndex].View.Center = new CGPoint(point.X - _dx[pageIndex], _pagerViewControllers[pageIndex].View.Center.Y);
        }

        #endregion

        #region Event handlers

        private void PanHandler()
        {
            if (!EnableSwipe) return;

            var directionCoef = _panGestureRecognizer.VelocityInView(_pagesContainer).X > 0 ? -1 : 1;

            if (((int) _panGestureRecognizer.State == (int) UIGestureRecognizerState.Began || (int) _panGestureRecognizer.State == (int) UIGestureRecognizerState.Changed) &&
                (_panGestureRecognizer.NumberOfTouches == 1))
            {
                var p0 = _panGestureRecognizer.LocationInView(_pagesContainer);
                var nextPageIndex = CurrentViewControllerIndex + directionCoef;
                var prevPageIndex = CurrentViewControllerIndex - directionCoef;
                if (prevPageIndex >= 0 && prevPageIndex < _pagerViewControllers.Count)
                    MovePage(prevPageIndex, p0);
                MovePage(CurrentViewControllerIndex, p0);
                if (nextPageIndex >= 0 && nextPageIndex < _pagerViewControllers.Count)
                    MovePage(nextPageIndex, p0);
            }
            else if ((int) _panGestureRecognizer.State == (int) UIGestureRecognizerState.Ended)
            {
                var swipeAddition = directionCoef * _pagesContainer.Frame.Width / 3;
                for (var i = 0; i < _pagerViewControllers.Count; i++)
                {
                    _dx[i] = 0;

                    if (_pagerViewControllers[i].View.Center.X >= swipeAddition &&
                        _pagerViewControllers[i].View.Center.X <= _pagesContainer.Frame.Width + swipeAddition)
                    {
                        SetCurrentPage(i, true);
                    }
                }
            }
        }

        #endregion
    }
}